#include "crypto_graphic.h"
#include "openssl/rsa.h"
#include "openssl/pem.h"
#include "../com/simple_logger.h"
#include "cstring"
#include <iostream>

using namespace std;

void CryptoGraphic::GenerateRSA(int bits)
{
    RSA *keypair = RSA_generate_key(bits, RSA_F4, NULL, NULL);
    BIO *prv_bio = BIO_new(BIO_s_mem());
    BIO *pub_bio = BIO_new(BIO_s_mem());

    PEM_write_bio_RSAPrivateKey(prv_bio, keypair, NULL, NULL, 0, NULL, NULL);
    PEM_write_bio_RSAPublicKey(pub_bio, keypair);

    int prv_key_len_ = BIO_pending(prv_bio);
    int pub_key_len_ = BIO_pending(pub_bio);

    char *prv_key_ = new char[prv_key_len_];
    char *pub_key_ = new char[pub_key_len_];

    BIO_read(prv_bio, prv_key_, prv_key_len_);
    BIO_read(pub_bio, pub_key_, pub_key_len_);
    prv_key_str_.assign(prv_key_, prv_key_len_);
    pub_key_str_.assign(pub_key_, pub_key_len_);

    RSA_free(keypair);
    BIO_free_all(prv_bio);
    BIO_free_all(pub_bio);

    delete[] prv_key_;
    delete[] pub_key_;
}

string CryptoGraphic::PrivateKey() const
{
    return prv_key_str_;
}

string CryptoGraphic::PublicKey() const
{
    return pub_key_str_;
}

string CryptoGraphic::DecryptRSA(string prv_key, const char *cipher_text, int cipher_text_len)
{
    RSA *rsa = RSA_new();
    BIO *prv = BIO_new_mem_buf(prv_key.c_str(), -1);
    rsa = PEM_read_bio_RSAPrivateKey(prv, &rsa, NULL, NULL);
    if (!rsa)
    {
        LOG4CPLUS_ERROR(SimpleLogger::Get()->LoggerRef(), "RSA 私钥创建失败");
        return "";
    }
    int rsa_len = RSA_size(rsa);
    char *plain_text = new char[rsa_len];
    memset(plain_text, 0, rsa_len);

    int plain_text_len = RSA_private_decrypt(rsa_len, (const unsigned char *)cipher_text, (unsigned char *)plain_text, rsa, RSA_PKCS1_PADDING);
    if (plain_text_len == -1)
    {
        LOG4CPLUS_ERROR(SimpleLogger::Get()->LoggerRef(), "私钥解码失败");
        return "";
    }
    if (plain_text_len == 0)
    {
        LOG4CPLUS_WARN(SimpleLogger::Get()->LoggerRef(), "PlainText is empty?");
    }
    string text;
    if (plain_text_len > 0)
    {
        text.assign(plain_text, plain_text_len);
    }
    delete[] plain_text;
    RSA_free(rsa);
    CRYPTO_cleanup_all_ex_data();
    return text;
}